#pragma once

#include "MBEnums.h"
#include "MBCameraView.h"
#include "MBDataTypes.h"
#include "MBDistanceMap.h"
#include "MBEnergyMinimizer.h"
#include "MBFileNotFoundException.h"
#include "MBIOException.h"
#include "MBInvalidArgumentException.h"
#include "MBLabelingOptions.h"
//#include "MBOctree.h"
#include "MBStringMethods.h"
#include "MBTriangle.h"
#include "MBTriangleLabelerBasedProjectOptions.h"
#include "MBVec3.h"

#include <map>
#include <set>
#include <string>
#include <vector>

/**
  *	An MBStringIgnoreCaseCompare object is used as a compare object for an stl
  * container with std::string objects. The case of the single characters is
  * ignored, so that e.g. the strings "string" and "STRING" are equal
  */
class MBStringIgnoreCaseCompare
{
public:
	/**
	  *	Returns true if the two strings are equal ignoring the case, false
	  * otherwise
	  *
	  * @param first first string in the comparison
	  * @param second second string in the comparison
	  * @return true if the two strings are equal ignoring the case, false otherwise
	  */
	bool operator()( const std::string &first, const std::string &second ) const
	{
		return MB::StringSmallerStringIgnoreCase( MB::ReplaceCharacter( first, ' ', '_' ), MB::ReplaceCharacter( second, ' ', '_' ) );
	}
};

/**
  *	An MBTriangleLabeler object is used for the labeling process. It needs
  * the paths of all necessary files and starts the labeling process when the
  * CalculateBestLabeling() method is invoked.
  */
class MBTriangleLabeler
{
public:
	MBTriangleLabeler(
		const std::string &modelFilePath,
		const MBModelFormat &modelFileFormat,
		const std::string &labeledModelFilePath,
		const MBModelFormat &labeledModelFileFormat,
		const MBTriangleLabelerBasedProjectOptions &basedProjectOptions,
		//const bool &useOctree,
		//const double &maxOctreeCellSideLength = 0.1,
		const bool &useOnlyCamerasInsideModelBBox = false ) throw( MBIOException, MBFileNotFoundException );

	~MBTriangleLabeler( void );

	bool CalculateBestLabeling(
		const MBLabelingOptions &options,
		MBDistanceMap *distanceMap ) throw( MBInvalidArgumentException );

	bool GenerateLabeledModelFile( MBDistanceMap *distanceMap ) const;
	void GenerateUndistortedImages( const MBBasedProjectFormat &projectFormat ) const;
	const std::vector<const MBCameraView*> *GetBestLabeling( void ) const;
	const std::vector<MBCameraView*> &GetCameraViews( void ) const;
	const std::string &GetInputModelFileBasePath( void ) const;
	const std::string &GetLabeledModelFilePath( void ) const;
	const std::string &GetRspFileBasePath( void ) const;
	const std::vector<MBTriangle*> *GetTriangles( void ) const;
	const std::vector<double*> &GetVertexCoordinates( void ) const;

	static void GenerateUndistortedImages(const std::string &undistortedImagesFilePath,
		const MBTriangleLabelerBasedProjectOptions &basedProjectOptions);

private:
	MBTriangleLabeler(const std::string &undistortedImagesFilePath);

	/** Base path of the underlying model file */
	std::string mModelFileBasePath;
	/** Full path of the generated labeled model */
	std::string mLabeledModelFilePath;
	MBModelFormat mLabeledModelFileFormat;
	/** Base path of the underlying RiScan Pro project (RSP) file */
	std::string mRspFileBasePath;
	/** Vector with all XYZ vertex coordinates. The coordinates are stored as pointers do double arrays  */
	std::vector<double*> mVertexCoordinates;
	/** Vector with pointers to all MBTriangle objects the model consists of */
	std::vector<MBTriangle*> mTriangles;
	/** Map, that maps vertex indices to sets, that contain all vertex indices, that correspond to one particular vertex */
	std::map<MBuint, std::set<MBuint>*> mEqualVertexIndexesMap;
	/** Const pointer to the vector of const pointers to camera views representing the calculated labeling for the model */
	const std::vector<const MBCameraView*> *mBestLabeling;
	/** Vector with pointers to all the camera views */
	std::vector<MBCameraView*> mCameraViews;
	/** Vector with pointers to all available camera models */
	std::vector<MBCameraModel*> mCameraModels;
	/** Boolean value indicating whether octree should be used or not */
	bool mUseOctree;
	/** Pointer to the Octree that contains all the triangle of the model */
	//MBOctree *mOctree;
	/** Min point of the model */
	MBVec3 mMinPoint;
	/** Max point of the model */
	MBVec3 mMaxPoint;
	/** Upper threshold for the smallest Octree-cell side length */
	double mMaxOctreeCellSideLength;
	/** Boolean value indicating whether only cameras with positions inside the axis aligned bounding box of the model should be involved in the labeling process or not */
	bool mUseOnlyCameraInsideModelBoundingBox;
	/** Pointer to the Energy minimizer object that calculates the best labeling */
	MBEnergyMinimizer *mMinimizer;

	void BuildOctree( void );
	void CalculateNormals( void );
	MBuint GetFirstElementFromEqualVertexIndexes( const MBuint &indexToken ) const;
	void LoadCameraViews(
		const MBTriangleLabelerBasedProjectOptions &basedProjectOptions );
	void LoadModelFile(
		const std::string &modelFilePath,
		const MBModelFormat &modelFileFormat ) throw( MBIOException );
	void LoadCameraViewMasks(
		const MBTriangleLabelerBasedProjectOptions &basedProjectOptions );
};